#include "FbxData.h"

namespace GC
{
	FbxData::FbxData(FbxNode* a_rootNode, FbxScene* a_scene)
	{		
		int f_meshCount = 0, f_skeletonCount = 0;		

		InitFbxDataNodes( a_rootNode, f_meshCount, f_skeletonCount);

		FbxGlobalSettings* f_globalSettings = &a_scene->GetGlobalSettings();
		m_frameRate = FbxGetFrameRate( f_globalSettings->GetTimeMode() );

		m_meshes.reserve( f_meshCount );
		m_skeletons.reserve( f_skeletonCount );
		m_animations.reserve( a_scene->GetSrcObjectCount<FbxAnimStack>() );				

		m_initialized = true;
	}

	FbxData::~FbxData()
	{				
	}
	
	/// <summary>
	/// Recursively go through all the FBX SDK nodes that exists
	/// </summary>
	/// <param name="a_node">The current node</param>
	/// <param name="a_meshCount">How many meshes have been found so far</param>
	/// <param name="a_skeletonCount">How many skeletons have been found so far</param>
	void FbxData::InitFbxDataNodes(FbxNode* a_node, int& a_meshCount, int& a_skeletonCount)
	{		
		FbxHelper::AddToMap( a_node );
		
		FbxNodeAttribute* f_attribute = a_node->GetNodeAttribute();		

		if (f_attribute != NULL)
		{
			FbxNodeAttribute::EType f_attributeType = f_attribute->GetAttributeType();

			switch (f_attributeType)
			{
				case FbxNodeAttribute::EType::eMesh:
					++a_meshCount;
					break;
				case FbxNodeAttribute::eSkeleton:
					++a_skeletonCount;
					break;
			}
		}

		int f_childCount = a_node->GetChildCount();

		for (int i = 0; i < f_childCount; ++i)
		{
			InitFbxDataNodes( a_node->GetChild(i), a_meshCount, a_skeletonCount );
		}		
	}

	/// <summary>
	/// Selects the proper HandleFunction depending on the node type
	/// </summary>
	/// <param name="a_node">The node to take care of</param>
	/// <param name="a_rootNode">The root node of the scene</param>
	/// <param name="a_manager">The FBX SDK manager, needed for mesh triangulation</param>	
	const bool FbxData::HandleNodes(FbxNode* a_node, const FbxNode* a_rootNode, FbxManager* a_manager)
	{
		try
		{
			FbxNodeAttribute* f_attribute = a_node->GetNodeAttribute();
			if (f_attribute != NULL)
			{
				FbxNodeAttribute::EType f_attributeType = f_attribute->GetAttributeType();

				//Time to see what function to call to save the data correctly
				switch (f_attributeType)
				{
					//Meshes
					case FbxNodeAttribute::eMesh:
						if (! HandleMeshNode( a_node, a_rootNode ,a_manager ) ) return false;
						break;
					//Skeleton / Skin
					case FbxNodeAttribute::eSkeleton:
						HandleSkeletonNode(a_node, a_rootNode);
						break;
				}
			}

			//To recursively loop through other nodes
			int f_childCount = a_node->GetChildCount();	
			for	(int i = 0; i < f_childCount; i++)
			{			
				HandleNodes(a_node->GetChild(i), a_rootNode ,a_manager );		
			}	
		}
		catch (std::exception e)
		{
			ConverterApp::LogMessage(L"FbxData: Failed to handle nodes");
			return false;
		}
		return true;
	}

	/// <summary>
	/// Handles the animations node to create an animation object
	/// </summary>
	/// <param name="a_scene">The a_scene.</param>
	/// <param name="a_rootNode">The a_root node.</param>
	/// <returns></returns>
	const bool FbxData::HandleAnimations(const FbxScene* a_scene, FbxNode* a_rootNode)
	{
		const int f_animationStacks = a_scene->GetSrcObjectCount<FbxAnimStack>();

		const double f_deltaFrameTime = 1 / m_frameRate;

		for ( int i = 0; i < f_animationStacks; ++i)
		{
			const FbxAnimStack* f_animationStack = a_scene->GetSrcObject<FbxAnimStack>(i);

			const int f_animLayerCount = f_animationStack->GetMemberCount<FbxAnimLayer>();

			GC::FbxAnimationPtr f_animation = std::make_shared<GC::FbxAnimation>(f_animationStack, f_deltaFrameTime, a_rootNode);

			m_animations.push_back(f_animation);
		}

		return true;
	}

	/// <summary>
	/// Handles a mesh node to create an mesh object and triangulates the mesh if needed
	/// </summary>
	/// <param name="a_meshNode">The FBX SDK mesh node.</param>
	/// <param name="a_rootNode">The root node.</param>
	/// <param name="a_manager">The a_manager that is needed for triangulation</param>
	/// <returns></returns>
	const bool FbxData::HandleMeshNode(FbxNode* a_meshNode, const FbxNode* a_rootNode, FbxManager* a_manager)
	{
		fbxsdk_2014_1::FbxMesh* f_fbxsdkMesh = a_meshNode->GetMesh();
		//alternative if above doesn't work: (fbxsdk_2014_1::FbxMesh*) a_meshNode->GetNodeAttribute();

		//Check if mesh is triangulated or not
		if (!f_fbxsdkMesh->IsTriangleMesh() )
		{
			FbxGeometryConverter f_geometryConverter = FbxGeometryConverter( a_manager );
			f_fbxsdkMesh = (fbxsdk_2014_1::FbxMesh*)f_geometryConverter.Triangulate( f_fbxsdkMesh, true );
			//If mesh is null it failed to triangulate.
			if (f_fbxsdkMesh == NULL)
			{
				ConverterApp::LogMessage(L"Failed: To triangulate a mesh ");
				//Throw exception to quit the HandleNodes list
				return false;
			}
		}

		GC::FbxMeshPtr f_mesh = std::make_shared<GC::FbxMesh>( a_meshNode, f_fbxsdkMesh, a_rootNode );

		if (!f_mesh->IsInitialized() ) return false;

		m_meshes.push_back( f_mesh );
		return true;
	}

	/// <summary>
	/// Logic for a skeleton node
	/// </summary>
	/// <param name="a_skeletonNode">The a_skeleton node.</param>
	/// <param name="a_rootNode">The a_root node.</param>
	void FbxData::HandleSkeletonNode(const FbxNode* a_skeletonNode, const FbxNode* a_rootNode)
	{
		GC::FbxSkeletonPtr f_skeleton = std::make_shared<GC::FbxSkeleton>(a_skeletonNode, a_rootNode);

		m_skeletons.push_back(f_skeleton);		
	}	
	
	/// <summary>
	/// Determines whether this instance is initialized.
	/// </summary>	
	const bool FbxData::IsInitialized()
	{
		return m_initialized;
	}
	/* GET FUNCTIONS TO READ PRIVATE VARIABLES  */
	const double FbxData::GetFrameRate() const
	{
		return m_frameRate;
	}

	const VectorFbxMeshPtr FbxData::GetMeshes() const
	{
		return m_meshes;
	}
	const size_t FbxData::GetMeshesSize() const
	{
		return m_meshes.size();
	}

	const VectorFbxSkeletonPtr FbxData::GetSkeletons() const
	{
		return m_skeletons;
	}
	const size_t FbxData::GetSkeletonsSize() const
	{
		return m_skeletons.size();
	}

	const VectorFbxAnimationPtr FbxData::GetAnimations() const
	{
		return m_animations;
	}
	const size_t FbxData::GetAnimationsSize() const
	{
		return m_animations.size();
	}

}